package cn.edu.xjtu.android.emoji_warfare_android.engine.worker;

import java.util.ArrayList;

import cn.edu.xjtu.android.emoji_warfare_android.engine.BaseEngine;
import cn.edu.xjtu.android.emoji_warfare_android.engine.SyncLinkedList;
import cn.edu.xjtu.android.emoji_warfare_android.engine.entity.Entity;

/**
 * CollideWorker<br>
 * 碰撞处理器，用于碰撞检测<br>
 * 主要用法：<br>
 * 1.将物理边界传入构造器<br>
 * 2.将物理时间间隔(两次碰撞检测之间的间隔)传入构造器或者使用默认<br>
 * 3.自己new一个CollideListener，覆写onCollide方法，返回值表示是否需要删除<br>
 * 4.使用addListener方法，传入需要检测的图层和监听器<br>
 * 5.根据需要设置回调(暂时没有需要设置的回调)<br>
 * 6.使用resume方法启动移动处理器<br>
 * 
 * @author bill
 *
 */
public class CollideWorker extends BaseWorker {
    /**
     * 默认两次碰撞检测之间的时间间隔
     */
    public final static long DEFAULT_COLLIDE_INTERVAL = 10;
    /**
     * 物理边界<br>
     * 用于在实体超出范围时自动删除
     */
    private final Entity physicsBoundary;
    /**
     * 所有的碰撞事件监听器。
     */
    private ArrayList<CollideRule> mCollides;

    /**
     * 构建一个碰撞处理器
     * 
     * @param engine
     *            引擎
     * @param physicsBoundary
     *            物理边界
     * @param interval
     *            碰撞检测时间间隔
     */
    public CollideWorker(BaseEngine engine, Entity physicsBoundary, long interval) {
        super(engine, interval);
        if (physicsBoundary == null) {
            throw new IllegalArgumentException("Physics boundary can't be null");
        }
        this.physicsBoundary = physicsBoundary;
        this.mCollides = new ArrayList<CollideRule>();
    }

    /**
     * 构建一个碰撞处理器，使用默认碰撞检测时间间隔
     * 
     * @param engine
     *            引擎
     * @param physicsBoundary
     *            物理边界
     */
    public CollideWorker(BaseEngine engine, Entity physicsBoundary) {
        this(engine, physicsBoundary, DEFAULT_COLLIDE_INTERVAL);
    }

    /**
     * 添加一个监听器
     * 
     * @param subjects
     *            主实体的BufferedLinkedList
     * @param objects
     *            客实体的BufferedLinkedList
     * @param listener
     *            碰撞监听器
     */
    public void addListener(SyncLinkedList<? extends Entity> subjects, SyncLinkedList<? extends Entity> objects,
            CollideListener listener) {
        mCollides.add(new CollideRule(this.physicsBoundary, subjects, objects, listener));
    }

    @Override
    public void run() {
        for (CollideRule l : mCollides) {
            l.check();
        }
    }

    /**
     * CollideRule<br>
     * 碰撞规则<br>
     * 由两个容器，一个CollideListener监听器组成<br>
     * 主要用法：<br>
     * 1.创建一个CollideListener，处理碰撞事件，返回值表示是否需要删除<br>
     * 2.调用CollideWorker的addListener方法，传入两个图层和Listener对象<br>
     * 注意：<br>
     * 第1条中所说的“删除”是仅仅在移动处理器对象列表中删除，不会对其他处理器造成影响
     * 
     * @author bill
     *
     */
    /* package */ class CollideRule {
        /**
         * 主体图层
         */
        private final SyncLinkedList<? extends Entity> subjects;
        /**
         * 客体图层
         */
        private final SyncLinkedList<? extends Entity> objects;
        /**
         * 物理边界<br>
         * 用于在目标超出范围时自动删除
         */
        private final Entity physicsBoundary;
        /**
         * 碰撞监听器
         */
        private final CollideListener mListener;

        /**
         * 构造一个碰撞规则
         * 
         * @param physicsBoundary
         *            物理引擎边界
         * @param subjects
         *            主体图层
         * @param objects
         *            客体图层
         */
        private CollideRule(Entity physicsBoundary, SyncLinkedList<? extends Entity> subjects,
                SyncLinkedList<? extends Entity> objects, CollideListener listener) {
            if (physicsBoundary == null) {
                throw new IllegalArgumentException("Physics boundary can't be null for CollideRule");
            }
            if (subjects == null) {
                throw new IllegalArgumentException("Subjectes can't be null for CollideRule");
            }
            if (objects == null) {
                throw new IllegalArgumentException("Objects can't be null for CollideRule");
            }
            if (listener == null) {
                throw new IllegalArgumentException("Listener can't be null for CollideRule");
            }
            this.physicsBoundary = physicsBoundary;
            this.subjects = subjects;
            this.objects = objects;
            this.mListener = listener;
        }

        /**
         * 进行一次碰撞检测，遍历两个图层进行检测。<br>
         * 该方法仅交由CollideWorker执行
         */
        private void check() {

            // 如果两个图层是空，这里会抛出错误
            Entity sbj, obj;
            synchronized (subjects) {
                synchronized (objects) {
                    // 检查碰撞
                    for (int i = 0; i < subjects.size(); i++) {
                        sbj = subjects.get(i);
                        for (int j = 0; j < objects.size(); j++) {
                            obj = objects.get(j);
                            if (sbj.isOverlap(obj)) {
                                mListener.onCollide(sbj, obj);
                            }
                        }
                    }
                    // 移除多余对象
                    for (int i = 0; i < subjects.size(); i++) {
                        sbj = subjects.get(i);
                        if (!physicsBoundary.isOverlap(sbj)) {
                            subjects.remove(i);
                            i--;
                        }
                    }
                    // 移除多余对象
                    for (int j = 0; j < objects.size(); j++) {
                        obj = objects.get(j);
                        if (!physicsBoundary.isOverlap(obj)) {
                            objects.remove(j);
                            j--;
                        }
                    }
                }
            }
        }
    }

    /**
     * CollideListener<br>
     * 碰撞监听器<br>
     * 当sbj和obj发生碰撞时执行的动作<br>
     * 
     * @author bill
     *
     */
    public interface CollideListener {
        /**
         * 当两个实体碰撞时触发的事件，该方法需要覆写。<br>
         * sbj和obj是两个实体的引用
         * 
         * @param sbj
         *            主实体
         * @param obj
         *            客实体
         */
        void onCollide(Entity sbj, Entity obj);
    }
}
